import java.awt.Point;

import pulpcore.animation.Fixed;
import pulpcore.image.CoreGraphics;
import pulpcore.image.CoreImage;
import pulpcore.sprite.Sprite;

/**
 * a map in the isometric projection
 */
class IsoMap extends Sprite {

	private int tileWidth;

	private int tileHeight;

	private int tileWidth2;

	private int tileHeight2;

	private int numTilesAxisX;

	private int numTilesAxisY;

	public final Fixed viewX = new Fixed(this);

	public final Fixed viewY = new Fixed(this);

	//private CoreImage[][] tileMap;

	private double tangent;

	/**
	 * numTilesAxisX is the number of lines that will be draw, from the top of
	 * the map to the right each line should be draw to the left, then skip to
	 * the next line, the next line is the axis X
	 * 
	 * constructor of the class
	 * 
	 * @param tileMap
	 * @param tileWidth
	 * @param tileHeight
	 */
	public IsoMap(/*CoreImage[][] tileMap,*/ int tileWidth, int tileHeight) {

		super(0, 0, tileWidth * /*tileMap.length*/8, tileHeight * /*tileMap[0].length*/12);

		//this.tileMap = tileMap;
		this.tileWidth = tileWidth;
		this.tileWidth2 = tileWidth / 2;
		this.tileHeight = tileHeight;
		this.tileHeight2 = tileHeight / 2;
		this.tangent = getTileWidth() / getTileHeight();

		numTilesAxisX = 8;//tileMap.length;
		numTilesAxisY = 12;//tileMap[0].length;

		pixelSnapping.set(true);
	}

	/**
	 * convert from screen coordinates to map coordinates know bug - when the
	 * map is not at the zero y, gives wrong coordinates
	 * 
	 * @param screenX
	 * @param screenY
	 * @return
	 */
	public Point getTileCoordsFromScreen(int screenX, int screenY) {

		// shift the X/Y based on the current position of the map
		screenX = screenX - this.x.getAsInt();
		screenY = screenY - this.y.getAsInt();

		// identify the box
		int xDiv = screenX / getTileWidth2();
		int yDiv = screenY / getTileHeight2();

		// left corner of the box
		int xCornerOfTheBox = xDiv * getTileWidth2();
		int yCornerOfTheBox = yDiv * getTileHeight2();

		// distance from the left corner of the box
		int xDistanceFromCornerOfTheBox = screenX - xCornerOfTheBox;
		int yDistanceFromCornerOfTheBox = screenY - yCornerOfTheBox;

		boolean above = isAbovetheTileLine(xDiv, yDiv,
				xDistanceFromCornerOfTheBox, yDistanceFromCornerOfTheBox);

		// the top tile
		int xTopTile = getMapWidth() / 2 / getTileWidth2();
		int yTopTile = 0;

		// shift from the top to the current piece of tile
		int shiftX = xDiv - xTopTile;
		int shiftY = yDiv - yTopTile;

		// piece of the NorthWest of the tile
		double x = shiftX + ((shiftY - shiftX) / 2.0);
		double y = (shiftY - shiftX) / 2.0;

		int xInt = (int) x;
		int yInt = (int) y;

		// identify if the piece of tile is NorthWest / SouthWest /
		// NorthEast
		// NorthWest
		if ((xInt + yInt) == (x + y)) {
			if (above) {
				xInt = xInt - 1;
			}
		} else {
			// southwest
			if (y > yInt) {
				if (!above) {
					yInt = yInt + 1;
				}
			}
			// northeast
			else {
				if (above) {
					yInt = yInt - 1;
				}
			}
		}

		return new Point(xInt, yInt);

	}

	/**
	 * based on the property of the triangle, tangent (tan) = opposite/adjacent,
	 * we will use to calculate the expected Y for a given X to identify if the
	 * point is above or under the tile line, and make the conversion from
	 * screen corrdinates to tile coodinates
	 * 
	 */
	private boolean isAbovetheTileLine(int xDiv, int yDiv,
			int xDistanceFromCornerOfTheBox, int yDistanceFromCornerOfTheBox) {

		boolean above = true;

		// calculate if the point is under or over the tile line
		boolean even = true;
		if ((xDiv + yDiv) % 2 > 0) {
			even = false;
		}

		int xToCalculateAdjacent = xDistanceFromCornerOfTheBox;
		int yToCalculateAdjacent = yDistanceFromCornerOfTheBox;

		if (even) {
			xToCalculateAdjacent = getTileWidth2()
					- xDistanceFromCornerOfTheBox;
		}

		/*
		 * tangent = oposite / adjacent adjacent => x => tangent = x / adjacent =>
		 * adjacent = x / tangent to identify to adjacent that reaches the line
		 * of the tile
		 */
		int adjacent = (int) (xToCalculateAdjacent / getTangent());

		if (yToCalculateAdjacent > adjacent) {
			above = false;
		}

		return above;
	}

	/**
	 * return the value of tangent
	 * 
	 * @return the tangent
	 */
	public double getTangent() {
		return tangent;
	}

	public int getMapWidth() {
		return this.tileWidth * numTilesAxisY;
	}

	public int getMapHeight() {
		return height.getAsInt();
	}

	public boolean isScrolling() {
		return viewX.isAnimating() || viewY.isAnimating();
	}

	public void update(int elapsedTime) {
		super.update(elapsedTime);
		viewX.update(elapsedTime);
		viewY.update(elapsedTime);
	}

	protected void drawSprite(CoreGraphics g) {
		// set the map to the horizontal center
		int x = getMapWidth() / 2;
		int y = 0;
		int fHalfTileWidth = tileWidth / 2;
		int fHalfTileHeight = tileHeight / 2;

		for (int j = 0; j < numTilesAxisX; j++) {

			// draw one line
			for (int i = 0; i < numTilesAxisY; i++) {
				//g.drawImage(tileMap[j][i], x, y);
				g.drawString(j + "," + i, x + 15, y + 15);

				x -= fHalfTileWidth;
				y += fHalfTileHeight;
			}

			// back to the original place
			x += ((numTilesAxisY) * fHalfTileWidth);
			y += ((numTilesAxisY) * -fHalfTileHeight);

			// shift to the next line
			x += fHalfTileWidth;
			y += fHalfTileHeight;
		}
	}

	/**
	 * return the value of tileHeight
	 * 
	 * @return the tileHeight
	 */
	public int getTileHeight() {
		return tileHeight;
	}

	/**
	 * return the value of tileWidth
	 * 
	 * @return the tileWidth
	 */
	public int getTileWidth() {
		return tileWidth;
	}

	/**
	 * return the value of tileHeight2
	 * 
	 * @return the tileHeight2
	 */
	public int getTileHeight2() {
		return tileHeight2;
	}

	/**
	 * return the value of tileWidth2
	 * 
	 * @return the tileWidth2
	 */
	public int getTileWidth2() {
		return tileWidth2;
	}

	/**
	 * center in the screen, if is bigger than the screen set the view port in
	 * the center of the map
	 * 
	 * @param width
	 * @param height
	 */
	public void centerInScreen(int width, int height) {

		System.out.println("map = " + getMapWidth() + "/" + getMapHeight());

		int xShiftInScreen = (width - getMapWidth()) / 2;
		int yShiftInScreen = (height - getMapHeight()) / 2;

		// make it proportional to the
		xShiftInScreen = ((int) (xShiftInScreen / getTileWidth()))
				* getTileWidth();
		yShiftInScreen = ((int) (yShiftInScreen / getTileHeight()))
				* getTileHeight();

		setLocation(xShiftInScreen, yShiftInScreen);
	}
}